'''
1、作用：在不改变原有函数的源代码前提下，给函数增加新的功能
       （装饰器符合开发中的封闭开放原则）
2、使用步骤：
     ① 定义一个装饰器（其本质是闭包）
          def check(fn):
              def inner():
                  print("登录验证。。。。。")
                  fn() # 使用外部函数的参数
          return inner

     ② 需要被装饰的函数
          def comment():
              print("发表评论！")

     ③ 使用装饰器装饰函数（增加功能）
          comment = check(comment)          结果：登录验证。。。。。
          comment()                              发表评论！

    或：
         def check(fn):
         def inner():
             print("登录验证。。。。。")
             fn()
         return inner

         @check    # 装饰器的语法糖用法：@装饰器名称，解释器遇到@check，会立即执行comment = check(comment)
         def comment():
             print("发表评论！")
         comment()
3、结论：装饰器就是把一个函数当作参数，传递给闭包中的外部函数，同时在内部函数中使用这个函数，并给他增加新的功能
4、执行时间的例子：
        import time
        def get_time(fn):
            def inner():
                start = time.time()
                fn()
                end = time.time()
                print("执行时间：", end - start)
            return inner
        @get_time
        def func():
            for i in range(100000):
                print(i)
        func()
5、通用装饰器：带有参数、带有返回值、带有不定长参数
TODO 装饰带有参数的函数

       def logging(fn):        # fn = sum_num
           def inner(a, b):
               fn(a, b)
           return inner       # sum_num = inner
       @logging
       def sum_num(a, b):
           result = a + b
           print(result)
       sum_num(1, 2)
TODO 装饰带有返回值的函数

         def logging(fn):
             def inner(a, b):
                 fn(a, b)
             return inner

         @logging
         def sum_num(a, b):
             result = a + b
             return result
         result = sum_num(1, 2)   # 装饰之后，函数就变成了内部函数inner，inner没有返回值，所以结果为None
         print(result)

          改为：

         def logging(fn):
             def inner(a, b):
                 return fn(a, b)

             return inner
         @logging
         def sum_num(a, b):
             result = a + b
             return result
         print(sum_num(1, 2))   # 注意两个重点：被装饰的函数作为参数传递给外部函数logging
                                             被装饰后的函数相当于内部函数inner
TODO 装饰带有不定长参数的函数

         def logging(fn):
             def inner(*args, **kwargs):
                 fn(*args, **kwargs)
             return inner
         @logging
         def sum_num(*args, **kwargs):
             print(args, kwargs)
         sum_num(1, 2, age="18")
TODO 多个装饰器的使用
=================================================
        def check1(fn1):
            def inner1():
                print("登录1。。。。。")
                fn1()
            return inner1
        def check2(fn2):
            def inner2():
                print("登录2。。。。。")
                fn2()
            return inner2
        @check1
        @check2
        def comment():
            print("发表评论！")          # 离函数最近的装饰器先装饰，然后外面的装饰器再进行装饰，由内到外的装饰过程
        comment()===================================================
TODO 带有参数的装饰器

# def logging(flag):       # 给装饰器传参，需要在装饰器外部再添加一个函数（功能：传递flag参数）
#
#      def decoretor(fn):  # 装饰器的外部函数只接受一个参数fn
#          def inner(a, b):
#              if flag == "+":
#                  print("正在加法运算。。。")
#              elif flag == "-":
#                  print("正在减法运算。。。")
#              return fn(a, b)
#          return inner
#      return decoretor
# @logging('-')
# def add(a, b):
#     return a + b
# def sub(a, b):
#     return a - b
# print(sub(1,3))

=====================================================================
TODO 类装饰器（使用较少，做了解）
总结：① 想要让类的实例对象能够像函数一样进行调用。需要在类里面使用call方法，把类的实例变成可调用对象
     ② 类装饰器装饰函数功能在call方法里面进行添加

# 类中call方法的使用：
class Check(object):
    def __call__(self, *args, **kwargs):
        print("我是call方法")
c = Check()
c()
==========================================================

class Check(object):
    def __int__(self, fn):
        self.__fn = fn
    def __call__(self, *args, **kwargs):
        print("登录")
        self.__fn()  # comment()
@Check  # comment = Check(comment)
def comment():
    print("发表评论！")
comment()
========================================

TODO property属性

# 一、装饰器方式
class Person(object):
    def __init__(self):
        self.__age = 0

    @property  # 获取属性
    def age(self):
        return self.__age

    @age.setter  # 修改属性
    def age(self, new_age):
        self.__age = new_age
p = Person()
print(p.age)  # 0
p.age = 100
print(p.age)  # 100
'''
#  二、类属性方式
# class Person(object):
#     def __init__(self):
#         self.__age = 0
#     def get_age(self):
#         return self.__age
#     def set_age(self, new_age):
#         if new_age >= 150:
#             print("年龄有误！")
#         else:
#             self.__age = new_age
#     age = property(get_age, set_age)
# p = Person()
# print(p.age)
# p.age = 100
# print(p.age)
